package com.quectel.taskserver.job.event;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.func.LambdaUtil;
import com.google.common.base.Preconditions;
import com.quectel.business.common.device.DeviceExtraAttrCls;
import com.quectel.business.dms.AccessorUtils;
import com.quectel.business.dms.application.ApplicationAccessConf;
import com.quectel.business.dms.application.access.accessor.ieg.AbstractIntelligentEntranceGuardAccessor;
import com.quectel.constant.core.device.DeviceConstants;
import com.quectel.constant.core.device.DeviceTypeCodeConstants;
import com.quectel.constant.core.entranceguard.EntranceGuardPersonConstants;
import com.quectel.constant.global.SystemConstants;
import com.quectel.core.module.application.dto.ApplicationDto;
import com.quectel.core.module.application.service.ApplicationService;
import com.quectel.core.module.device.dto.DeviceDto;
import com.quectel.core.module.device.service.DeviceService;
import com.quectel.core.module.entranceguard.dto.EntranceGuardPersonDto;
import com.quectel.core.module.entranceguard.dto.mongo.MongoPersonAccessRecordDto;
import com.quectel.core.module.entranceguard.service.EntranceGuardPersonService;
import com.quectel.core.module.household.dto.HouseholdDto;
import com.quectel.core.module.household.service.HouseholdService;
import com.quectel.sdk.hik8700.Hik8700ClientBuilder;
import com.quectel.sdk.hik8700.entranceguard.EntranceGuardClient;
import com.quectel.sdk.hik8700.entranceguard.model.AccessRecordResp;
import com.quectel.sdk.hik8700.entranceguard.model.PersonInfoResp;
import com.quectel.util.common.DateUtils;
import com.quectel.util.common.JacksonUtils;
import com.quectel.util.common.MyFileUtils;
import com.quectel.util.kit.StorageKit;
import com.quectel.util.redis.RedisUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.shardingsphere.elasticjob.api.ShardingContext;
import org.apache.shardingsphere.elasticjob.dataflow.job.DataflowJob;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.io.File;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 海康8700 出入记录抓取定时任务
 *
 * @author: rananxin
 * @email: ryan.ran@quectel.com
 * @date: 2022-11-09 16:16:00
 */
@Component
public class Hik8700EventFetchTask implements DataflowJob<ApplicationDto> {

    @DubboReference
    private ApplicationService applicationService;
    @DubboReference
    private DeviceService deviceService;
    @DubboReference
    private EntranceGuardPersonService entranceGuardPersonService;
    @DubboReference
    private HouseholdService householdService;

    private static final Logger LOGGER = LoggerFactory.getLogger(Hik8700EventFetchTask.class);

    private static final String HIK_8700_FETCH_EVENT_EXEC_LIMIT = "HIK_8700_fetch_event_exec_limit:";
    private static final String HIK_8700_FETCH_FACE_RECORD_LAST_TIME = "HIK_8700_fetch_face_record_last_time:";

    @Override
    public List<ApplicationDto> fetchData(ShardingContext shardingContext) {
        List<ApplicationDto> applicationDtos = applicationService
                .fetchByConfCode(ApplicationAccessConf.ConfCodeEnum.HIK8700.getCode()
                        , shardingContext.getShardingTotalCount()
                        , shardingContext.getShardingItem());
        return applicationDtos.stream().filter(o -> DeviceTypeCodeConstants.HIK_8700_ENTRANCE_GUARD.equals(o.getDeviceType().getCode())).collect(Collectors.toList());

    }

    @Override
    public void processData(ShardingContext shardingContext, List<ApplicationDto> list) {
        boolean setnx = RedisUtils.setnx(HIK_8700_FETCH_EVENT_EXEC_LIMIT + shardingContext.getShardingItem(), "", 30, TimeUnit.MINUTES);
        if (setnx) {
            final Date currentDate = new Date();
            try {
                for (ApplicationDto application : list) {
                    try {
                        ApplicationAccessConf.TemplateOfHik8700Conf templateOfHik8700Conf = ApplicationAccessConf
                                .parseExtStr(ApplicationAccessConf.TemplateOfHik8700Conf.class, application.getAccessConf());

                        assert templateOfHik8700Conf != null;
                        EntranceGuardClient build = Hik8700ClientBuilder.builder()
                                .key(templateOfHik8700Conf.getKey())
                                .secret(templateOfHik8700Conf.getSecret())
                                .host(templateOfHik8700Conf.getHost())
                                .build(EntranceGuardClient.class);

                        Date lastDate = null;
                        String lastDateStr = RedisUtils.get(HIK_8700_FETCH_FACE_RECORD_LAST_TIME + application.getId());
                        if (StringUtils.isBlank(lastDateStr)
                                || DateUtil.between(DateUtils.parse(lastDateStr, DateUtils.FormatType.COMMON), currentDate, DateUnit.HOUR, false) > 6) {
                            lastDate = DateUtil.offsetHour(currentDate, -6);
                        } else {
                            lastDate = DateUtils.parse(lastDateStr, DateUtils.FormatType.COMMON);
                        }

                        // 准确查找设备 该应用下同设备类型非游离设备
                        Map<String, Object> seekFitDeviceParams = new HashMap<>();
                        seekFitDeviceParams.put(LambdaUtil.getFieldName(DeviceDto::getApplication), application.getId());
                        seekFitDeviceParams.put(DeviceConstants.QueryParamEnum.NOT_FREE.getParamKey(), SystemConstants.NORMAL.toString());
                        seekFitDeviceParams.put(LambdaUtil.getFieldName(DeviceDto::getDeviceTypeId), application.getDeviceTypeId());
                        List<DeviceDto> deviceDtoList = deviceService.queryList(seekFitDeviceParams);

                        assert lastDate != null;
                        build.fetchFaceRecord(deviceDtoList.stream()
                                        .map(DeviceDto::getUniqueId)
                                        .collect(Collectors.toList())
                                , lastDate, currentDate, rs -> recordHand(application, rs));

                        RedisUtils.set(HIK_8700_FETCH_FACE_RECORD_LAST_TIME + application.getId()
                                , DateUtils.format(currentDate, DateUtils.FormatType.COMMON)
                                , 15, TimeUnit.DAYS);
                    } catch (Exception e) {
                        LOGGER.error("海康8700 进出记录抓取异常", e);
                    }
                }
            } finally {
                RedisUtils.del(HIK_8700_FETCH_EVENT_EXEC_LIMIT + shardingContext.getShardingItem());
            }
        }
    }

    private void recordHand(ApplicationDto application, List<AccessRecordResp> resultsList) {
        if (CollectionUtils.isEmpty(resultsList)) {
            return;
        }

        for (AccessRecordResp resultsCls : resultsList) {
            try {
                List<DeviceDto> deviceDtos = deviceService.selectCacheByUniqueId(resultsCls.getDoorUuid());
                if (CollUtil.isEmpty(deviceDtos)) {
                    continue;
                }

                for (DeviceDto deviceDto : deviceDtos) {
                    if (deviceDto.getVillageId() == null) {
                        continue;
                    }

                    File file = MyFileUtils.downloadToTempDir(resultsCls.getPicUrl(), false);
                    String url = StorageKit.uploadToPublicBucketObtainUrl(file);

                    MongoPersonAccessRecordDto personAccessRecordDto = new MongoPersonAccessRecordDto();
                    personAccessRecordDto.setTenantId(deviceDto.getTenantId());
                    personAccessRecordDto.setVillageId(deviceDto.getVillageId());
                    personAccessRecordDto.setBuildingId(deviceDto.getBuildingId());
                    personAccessRecordDto.setDeviceCategoryId(deviceDto.getDeviceCategoryId());
                    personAccessRecordDto.setDeviceTypeId(deviceDto.getDeviceTypeId());
                    personAccessRecordDto.setDeviceId(deviceDto.getId());
                    personAccessRecordDto.setDeviceName(deviceDto.getName());


                    //优先查询数据库是否已经绑定关系
                    EntranceGuardPersonDto bindPerson = entranceGuardPersonService.queryAuthBy(deviceDto.getId(), String.valueOf(resultsCls.getPersonId()));
                    if (bindPerson != null) {
                        if (EntranceGuardPersonConstants.PersonTypeEnum.HOUSEHOLD.getType().equals(bindPerson.getPersonType())) {
                            HouseholdDto householdDto = householdService.selectById(bindPerson.getPersonId());
                            if (householdDto != null) {
                                personAccessRecordDto.setPersonType(EntranceGuardPersonConstants.PersonTypeEnum.HOUSEHOLD.getType());
                                personAccessRecordDto.setPersonId(householdDto.getId());
                                personAccessRecordDto.setPersonName(householdDto.getName());
                                personAccessRecordDto.setPersonMobile(householdDto.getMobile());

                            }
                        }
                    } else {

                        ApplicationAccessConf.TemplateOfHik8700Conf templateOfHik8700Conf = ApplicationAccessConf
                                .parseExtStr(ApplicationAccessConf.TemplateOfHik8700Conf.class, application.getAccessConf());

                        assert templateOfHik8700Conf != null;
                        EntranceGuardClient build = Hik8700ClientBuilder.builder()
                                .key(templateOfHik8700Conf.getKey())
                                .secret(templateOfHik8700Conf.getSecret())
                                .host(templateOfHik8700Conf.getHost())
                                .build(EntranceGuardClient.class);

                        PersonInfoResp personInfoResp = build.personInfoById(String.valueOf(resultsCls.getPersonId()));
                        if (personInfoResp != null) {
                            personAccessRecordDto.setPersonType(EntranceGuardPersonConstants.PersonTypeEnum.HOUSEHOLD.getType());

                            HouseholdDto householdDto = householdService.selectCacheByVillageIdAndMobile(deviceDto.getVillageId(), personInfoResp.getPhoneNo());
                            if (householdDto == null) {
                                personAccessRecordDto.setPersonId(null);
                                personAccessRecordDto.setPersonName(resultsCls.getPersonName());
                                personAccessRecordDto.setPersonMobile(personInfoResp.getPhoneNo());
                            } else {
                                personAccessRecordDto.setPersonId(householdDto.getId());
                                personAccessRecordDto.setPersonName(householdDto.getName());
                                personAccessRecordDto.setPersonMobile(householdDto.getName());

                                EntranceGuardPersonDto saveObj = new EntranceGuardPersonDto();

                                saveObj.setTenantId(deviceDto.getTenantId());
                                saveObj.setAuthType(EntranceGuardPersonConstants.AuthTypeEnum.ADD.getAuthType());
                                saveObj.setDeviceId(deviceDto.getId());
                                saveObj.setPersonType(EntranceGuardPersonConstants.PersonTypeEnum.HOUSEHOLD.getType());
                                saveObj.setPersonId(householdDto.getId());
                                saveObj.setIdentityAtDevice(String.valueOf(resultsCls.getPersonId()));
                                saveObj.setAuthStatus(EntranceGuardPersonConstants.AuthStatusEnum.SEND_SUCCESS.getAuthStatus());
                                saveObj.setStartTime(new Date());
                                saveObj.setEndTime(DateUtil.offsetDay(new Date(), 365 * 10));
                                saveObj.setCreateTime(new Date());
                                entranceGuardPersonService.save(saveObj);
                            }

                        }
                    }


                    personAccessRecordDto.setAccessMode((byte) 4);

                    DeviceExtraAttrCls.AttrOfIntelligentEntranceGuard attrOfIntelligentEntranceGuard = DeviceExtraAttrCls.parseExtStr(DeviceExtraAttrCls.AttrOfIntelligentEntranceGuard.class
                            , deviceDto.getExtraAttr());

                    if (attrOfIntelligentEntranceGuard != null) {
                        personAccessRecordDto.setOutOrIn(attrOfIntelligentEntranceGuard.getOutOrIn());
                    }
                    personAccessRecordDto.setSnapImages(JacksonUtils.toJsonString(Collections.singletonList(url)));
                    personAccessRecordDto.setEventTime(new Date(resultsCls.getEventTime()));
                    personAccessRecordDto.setLongitude(deviceDto.getLongitude());
                    personAccessRecordDto.setLatitude(deviceDto.getLatitude());

                    AbstractIntelligentEntranceGuardAccessor entranceGuardAccessor = AccessorUtils.getIntelligentEntranceGuardAccessor(deviceDto.getDeviceType());
                    Preconditions.checkNotNull(entranceGuardAccessor);
                    entranceGuardAccessor.accessRecordHand(personAccessRecordDto);
                }
            } catch (Exception e) {
                LOGGER.error("海康8700 进出记录处理异常", e);
            }
        }
    }
}
